package org.ponly.lpf4j.impl;

import org.ponly.lpf4j.ExtensionRegistry;
import org.ponly.lpf4j.Plugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 */
class PluginContext implements ExtensionRegistry {
    private static final Logger LOG = LoggerFactory.getLogger(PluginContext.class);

    private final Map<Class<?>, List<?>> extensionsMap = new HashMap<>();
    private final Plugin plugin;
    private final ExtensionRegistry delegate;

    public PluginContext(Plugin plugin, ExtensionRegistry delegate) {
        this.plugin = plugin;
        this.delegate = delegate;
    }

    @Override
    public <E> void registerExtension(Class<E> extensionPoint, E extension) {
        delegate.registerExtension(extensionPoint, extension);
        addPointExtension(extensionPoint, extension);

        LOG.debug("Plugin '{}' register extension: {}", plugin, extension);
    }

    @Override
    public <E> void registerExtension(Class<E> extensionPoint, E extension, int loadingOrder) {
        delegate.registerExtension(extensionPoint, extension, loadingOrder);
        addPointExtension(extensionPoint, extension);

        LOG.debug("Plugin '{}' register extension: {} at {}", plugin, extension, loadingOrder);
    }

    @Override
    public <E> void unregisterExtension(Class<E> extensionPoint, E extension) {
        delegate.unregisterExtension(extensionPoint, extension);
        removePointExtension(extensionPoint, extension);

        LOG.debug("Plugin '{}' unregister extension: {}", plugin, extension);
    }

    @Override
    public boolean hasAnyExtensions(Class<?> extensionPoint) {
        return delegate.hasAnyExtensions(extensionPoint);
    }

    @Override
    public <E> E getExtension(Class<E> extensionPoint) {
        return delegate.getExtension(extensionPoint);
    }

    @Override
    public <E> Collection<E> getExtensions(Class<E> extensionPoint) {
        return delegate.getExtensions(extensionPoint);
    }

    private <E> void addPointExtension(Class<E> point, E extension) {
        List<E> extensions = (List<E>) extensionsMap.get(point);
        if (null == extensions) {
            extensions = new ArrayList<E>();
            extensionsMap.put(point, extensions);
        }
        extensions.add(extension);
    }

    private <E> void removePointExtension(Class<E> point, E extension) {
        List<E> extensions = (List<E>) extensionsMap.get(point);
        if (null != extensions) {
            extensions.remove(extension);
            if (extensions.isEmpty()) {
                extensionsMap.remove(point);
            }
        }
    }

    public Plugin getPlugin() {
        return plugin;
    }

    public void destroy() {
        try {
            plugin.destroy(this);
        } catch (Throwable ex) {
            // ignore
            LOG.warn("Plugin '{}' destroy error: ", plugin, ex);
        }
        for (Map.Entry<Class<?>, List<?>> entry : extensionsMap.entrySet()) {
            Class<?> point = entry.getKey();
            List<?> extensions = entry.getValue();
            if (null != extensions) {
                for (Object extension : extensions) {
                    delegate.unregisterExtension((Class) point, extension);
                }
            }
        }
        extensionsMap.clear();
    }
}
